10. 集合

Collection

概述

在 java.util.Collection 中。是层级结构中的根接口,表示一组对象。Collection 是接口,但是在 JDK 中不提供此接口的任何实现,它提供了更为具体的子接口(如 Set,List)

Collection常用方法

集合中的 contains,remove 方法等涉及到对象比较的方法,都是用对象的 equals 方法。如果对象没有重写equals 方法,那么默认的 equals 方法的作用和 == 是一样的,都是比较对象的地址。

1. 添加

add(object obj): 一个添加一个元素

addAll(Collection other): 一次添加多个元素。理解为并集

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
package com.itguigu.collection;

import java.util.ArrayList;
import java.util.Collection;

import org.junit.Test;

public class TestConlection {
@Test
public void test1() {
// 多态。ArrayList 是 Collection 子接口 List 的实现类。
Collection arrayList = new ArrayList();
// 增加单个元素
arrayList.add("张三");
// 增加单个元素
arrayList.add("李四");

Collection other = new ArrayList();
other.add("王五");
other.add("赵六");

// 增加另外一个 collection 集合。
arrayList.addAll(other);

System.out.println(arrayList.size()); // 4
}
}
2. 删除

remove(object obj): 一次移出一个元素

removeAll(Collection other):一个移出多个元素。理解为移出多个相同的元素。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
package com.itguigu.collection;

import java.util.ArrayList;
import java.util.Collection;

import org.junit.Test;

public class TestConlection {
@Test
public void test1() {
Collection arrayList = new ArrayList();
arrayList.add("张三");
arrayList.add("李四");
arrayList.add("王五");
System.out.println(arrayList.size()); // 3

// 移出单个元素
arrayList.remove("张三");
System.out.println(arrayList.size()); // 2

Collection other = new ArrayList();
other.add("王五");
other.add("赵六");

// 一次移出多个元素。王五被移除
arrayList.removeAll(other);
System.out.println(arrayList.size()); // 1
}
}

clear(): 清除所有元素

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package com.itguigu.collection;

import java.util.ArrayList;
import java.util.Collection;

import org.junit.Test;

public class TestConlection {
@Test
public void test1() {
Collection arrayList = new ArrayList();
arrayList.add("张三");
arrayList.add("李四");
arrayList.add("王五");

arrayList.clear(); // 清空所有元素
System.out.println(arrayList.size());
}
}
3. 修改

Collection 根接口中没有提供修改的方法,子接口中可能有

4. 查询

contains(object obj): 判断 obj 是否在当前集合中

containsAll(Collection c): 判断 c 中的元素是否都在当前集合中

isEmpty(): 判断是否为空

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package com.itguigu.collection;

import java.util.ArrayList;
import java.util.Collection;

import org.junit.Test;

public class TestConlection {
@Test
public void test1() {
Collection arrayList = new ArrayList();
arrayList.add("张三");
arrayList.add("李四");
arrayList.add("王五");

Collection other = new ArrayList();
other.add("张三");
other.add("田七");

System.out.println(arrayList.contains("张三")); // true
System.out.println(arrayList.contains(other)); // false
}
}

Collection 根接口中没有提供获取一个元素的方法。

5. 遍历

toArray: 如果该集合的底层是数组实现,那么效率较高,否则则较低。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package com.itguigu.collection;

import java.util.ArrayList;
import java.util.Collection;

import org.junit.Test;

public class TestConlection {
@Test
public void test1() {
Collection arrayList = new ArrayList();
arrayList.add("张三");
arrayList.add("李四");

Object[] array = arrayList.toArray();
for (int i = 0; i < array.length; i++) {
System.out.println(array[i]);
}
}
}

foreach:可以称为增强版的 for 循环。如果只是查看数组或集合的元素,那么用 foreach 比较简单,如果涉及删除,修改,那么就不使用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package com.itguigu.collection;

import java.util.ArrayList;
import java.util.Collection;

import org.junit.Test;

public class TestConlection {
@Test
public void test1() {
Collection arrayList = new ArrayList();
arrayList.add("张三");
arrayList.add("李四");
arrayList.add("王五");

for (Object object : arrayList) {
System.out.println(object);
}
}
}

Iterator: 迭代器。在 java.util.Iterator。用于遍历和循环集合使用。只支持遍历 Collection 集合,不支持数组。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package com.itguigu.collection;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;

import org.junit.Test;

public class TestConlection {
@Test
public void test1() {
Collection arrayList = new ArrayList();
arrayList.add("张三");
arrayList.add("李四");
arrayList.add("王五");

Iterator iterator = arrayList.iterator();
while (iterator.hasNext()) {
// iterator.next() 获取下一个元素。iterator.remove() 删除元素。
Object object = (Object) iterator.next();
System.out.println(object);
}
}
}
6. 其他

retainAll(Collection c):获取两个集合的交集

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package com.itguigu.collection;

import java.util.ArrayList;
import java.util.Collection;

import org.junit.Test;

public class TestConlection {
@Test
public void test1() {
Collection arrayList = new ArrayList();
arrayList.add("张三");
arrayList.add("李四");
arrayList.add("王五");

Collection other = new ArrayList();
other.add("张三");
other.add("田七");

arrayList.retainAll(other);
System.out.println(arrayList.size()); // 1
}
}

List

列表:可重复的,按顺序存储。也是接口,位置是 java.util.List

实现类:ArrayList(动态数组)

特点:
  1. 有序。可以使用索引精确的控制

  2. 可重复

常用方法:

因为是 Collection 的子接口,所有 Collection 有的方法,它都有。除此之外,还有很多和索引相关的方法。

add(index, Element): 在指定的 index 位置插入元素

addAll(index, Collection): 在指定的 index 位置插入多个元素。

remove(index): 删除指定位置的元素

set(index, Element): 修改指定位置的元素。collection 中是没有提供修改的方法的。

indexOf(object): 查询元素在集合中的位置,返回索引

lastindexOf(object): 查询元素在集合中的最后一个位置,返回索引

get(index): 返回 index 位置的元素

subList(fromIndex, toIndex): 截取,范围是 [fromIndex, toIndex)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
package com.itguigu.collection;

import java.util.ArrayList;
import java.util.List;

import org.junit.Test;

public class TestConlection {
@Test
public void test1() {
List list = new ArrayList();
list.add("张三"); // 父接口中的 add 方法
list.add(0, "李四"); // list 中的 add方法,在指定位置插入元素

List other = new ArrayList();
other.add("王五");
other.add("赵六");
other.add(0, "李四");

list.addAll(0, other);
list.remove(0);
list.set(0, "吴十");

System.out.println(list.get(0));
System.out.println(list.indexOf("吴十"));
System.out.println(list.lastIndexOf("李四"));

for (Object object : list) {
System.out.println(object);
}
}
}

collection 中遍历集合中的元素有三种方法,分别是 for,foreach 和 Iterator。在 List 中还可以使用 ListIterator,它是 Iterator 的子接口,拥有 Iterator 的所有方法。除此之外遍历还可以指定方向,指定位置,还新增了 add 方法,set 方法等。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
package com.itguigu.collection;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;

import org.junit.Test;

public class TestConlection {
@Test
public void test1() {
List list = new ArrayList();
list.add("张三");
list.add(0, "李四");
list.add("王五");
list.add("赵六");
list.add(0, "李四");

// 从前往后遍历
ListIterator listIterator = list.listIterator();
while (listIterator.hasNext()) {
Object object = (Object) listIterator.next();
System.out.println(object);
}

System.out.println("---------------------");

// 从后往前遍历,list.size() 用于指定遍历的开始位置
ListIterator listIterator2 = list.listIterator(list.size());
while (listIterator2.hasPrevious()) {
Object object = (Object) listIterator2.previous();
System.out.println(object);
}

}
}
常见实现类

Veactor: 动态数组,旧版本,线程安全的。内部实现为数组,初始化大小为 10。扩容时,扩容为原来的2倍。

ArrayList: 动态数组,新版本,线程安全的。内部实现为数组,初始化大小为 10。扩容时,扩容为原来的1.5倍。【使用频率较高】

LinkedList: 双向链表,双端队列。内部实现为链表。【使用频率较高】

Stack: 栈,Veactor 的子类。内部实现为数组。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package com.itguigu.collection;

import java.util.LinkedList;

import org.junit.Test;

public class TestList {
@Test
public void test() {
LinkedList linkedList = new LinkedList();
linkedList.offerFirst(1); // 在前面添加元素
linkedList.offerFirst(2);

linkedList.offerLast(3); // 在后面添加元素
linkedList.offerLast(4);

for (Object object : linkedList) {
System.out.println(object);
}
}
}

动态数组和 LinkedList 的区别是什么?

  1. 内部实现不同,动态数组底层为数组。LinkedList 底层为链表
  2. 动态数组对索引相关的操作效率很高,LinkedList 则较低
  3. 动态数组的插入,删除涉及到元素的移动,LinkedList 的删除,插入只涉及到前后元素的关系。
  4. 如果是操作索引更多,那么可以选择动态数组。如果是添加删除,插入等操作,那么就选择链表。

Set

集:不可重复的,无序的(和添加顺序无关)。位置为 java.util.Set,是 Collection 的子接口

特点:
  1. 元素不重复
  2. 无序的(和添加顺序无关)
常见方法:

因为是 Collection 的子接口,所有 Collection 有的方法,它都有。Set 没有自己新增方法。

常见实现类:

HashSet:完全无序。保证元素不重复是靠对象的 equals 方法。所以在使用 HashSet 的时候需要重写对象的 equals 方法。

TreeSet: 可以按照大小排序,和添加顺序无关。保证元素不重复是靠 Comparable 接口的 compareTo 方法,或者其他的比较方法,例如 java.util.Comparator。例如两个李四都是 36 岁,但是第一个李四成绩为 99 分,第二个为 88 分。假如 compareTo 方法实现的功能是比较年纪,所以就认为这两个对象是相同的,那么第二个李四就不会添加进入集合当中。

LinkedHashSet:遍历的时候可以保证顺序是添加时候的顺序,但是存储和添加顺序无关。LinkedHashSet 是 HashSet 的子类,但是要比 HashSet 多维护一个添加的顺序,所以效率比 HashSet 要低。保证元素不重复是靠对象的 equals 方法。所以在使用 HashSet 的时候需要重写对象的 equals 方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
package com.itguigu.collection;

import java.util.Comparator;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.TreeSet;


import org.junit.Test;

public class TestList {
@SuppressWarnings("all")
@Test
public void test() {
// hashSet: 元素不重复
HashSet hashSet = new HashSet();
hashSet.add("张三");
hashSet.add("李四");
hashSet.add("王五");
hashSet.add("李四");

for (Object object : hashSet) {
System.out.println(object); // 李四 张三 王五
}
System.out.println("-------------------");
}

@SuppressWarnings("all")
@Test
public void test2() {
// treeSet:元素不重复,且可以排序,需要实现 Student compareTo 方法
TreeSet treeSet = new TreeSet();
treeSet.add(new Student("张三", 99, 20));
treeSet.add(new Student("李四", 80, 33));
treeSet.add(new Student("王五", 76, 19));
// 判断元素想不相同是使用 equals 方法,如果 Student 没有重写,那么默认和 == 一样。比较的是地址
// 这里地址肯定是不一样的,所以 treeSet 中肯定有两个李四的对象
// 所以我们需要实现 Student 的 hashCode 和 equals 方法。这样才不会有相同的 李四对象
treeSet.add(new Student("李四", 80, 33));

for (Object object : treeSet) {
System.out.println(object);
}
System.out.println("-------------------");
}


@SuppressWarnings("all")
@Test
public void test3() {
// treeSet:传入自定义比较器
TreeSet treeSet = new TreeSet(new Comparator() {
@Override
public int compare(Object o1, Object o2) {
Student student1 = (Student) o1;
Student student2 = (Student) o2;
return student1.getAge() - student2.getAge();
}
});
treeSet.add(new Student("张三", 99, 20));
treeSet.add(new Student("李四", 80, 33));
treeSet.add(new Student("王五", 76, 19));
treeSet.add(new Student("李四", 80, 33));

for (Object object : treeSet) {
System.out.println(object);
}
System.out.println("-------------------");
}

@SuppressWarnings("all")
@Test
public void test4() {
// LinkedHashSet:元素不重复,且顺序为添加时候的顺序
LinkedHashSet linkedHashSet = new LinkedHashSet();
linkedHashSet.add(new Student("李四", 80, 33));
linkedHashSet.add(new Student("王五", 76, 19));
linkedHashSet.add(new Student("张三", 99, 20));

for (Object object : linkedHashSet) {
System.out.println(object);
}
System.out.println("-------------------");
}
}


class Student implements Comparable{
private String name;
private int score;
private int age;
public Student(String name, int score, int age) {
super();
this.name = name;
this.score = score;
this.age = age;
}
public Student() {
super();
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getScore() {
return score;
}
public void setScore(int score) {
this.score = score;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}

@Override
public String toString() {
return "Student [name=" + name + ", score=" + score + ", age=" + age + "]";
}

@Override
public int compareTo(Object o) {
Student student = (Student) o;
// 按照成绩大小排序
return this.score - student.score;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + age;
result = prime * result + ((name == null) ? 0 : name.hashCode());
result = prime * result + score;
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Student other = (Student) obj;
if (age != other.age)
return false;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
if (score != other.score)
return false;
return true;
}
}

结论:

  1. 如果既要元素不重复,又要可以支持大小排序,那么使用 TreeSet
  2. 如果既要元素不重复,又要保证添加顺序,那么可以使用 LinkedHashSet
  3. 如果只是需要元素不重复,那么 HashSet 既可。

Map

Java.util.Map接口与Collection 最大的不同就是 Map 存储的为 键值对。

Map常用方法

1. 添加

put(key, value)

putAll(Map map)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package com.itguigu.Map;

import java.io.BufferedWriter;
import java.util.HashMap;

public class TestMap {
@SuppressWarnings("all")
public static void main(String[] args) {
HashMap hashMap = new HashMap();
// 一次添加一个映射关系
hashMap.put("key", "value");
hashMap.put("key1", null);
hashMap.put("key2", new String[] {"value1", "value2"});

HashMap hashMap2 = new HashMap();
hashMap2.put("key3", "value3");
hashMap2.put("key4", "value4");

// 一次添加多个映射关系
hashMap.putAll(hashMap2);

System.out.println(hashMap.size()); // 5
}
}
2. 删除

remove(Object Key): 根据 key 删除

clear(): 清空

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package com.itguigu.Map;

import java.util.HashMap;

public class TestMap {
@SuppressWarnings("all")
public static void main(String[] args) {
HashMap hashMap = new HashMap();
// 一次添加一个映射关系
hashMap.put("key", "value");
hashMap.put("key1", null);
hashMap.put("key2", new String[] {"value1", "value2"});
// remove
hashMap.remove("key");
System.out.println(hashMap.size()); // 2
// clear
hashMap.clear();
System.out.println(hashMap.size()); // 0
}
}
3.修改:

通过 put 可以替换 value

1
2
3
4
5
6
7
8
9
10
11
12
13
14
package com.itguigu.Map;

import java.util.HashMap;

public class TestMap {
@SuppressWarnings("all")
public static void main(String[] args) {
HashMap hashMap = new HashMap();
// 一次添加一个映射关系
hashMap.put("key", "value");
// put key 相同就等于替换
hashMap.put("key", "new value");
}
}
4. 查询

containsKey(Object key): 判断 key 是否存在

containsValue(Object value): 判断 value 是否存在

get(Object key): 根据 key 获取 value

isEmpty(): 判断是否为空

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package com.itguigu.Map;

import java.util.HashMap;

public class TestMap {
@SuppressWarnings("all")
public static void main(String[] args) {
HashMap hashMap = new HashMap();
// 一次添加一个映射关系
hashMap.put("key", "value");
hashMap.put("key2", "value2");
hashMap.put("key3", "value3");

System.out.println(hashMap.containsKey("key3")); // true
System.out.println(hashMap.containsValue("value2"));// true
System.out.println(hashMap.isEmpty()); // false
System.out.println(hashMap.get("key")); // value
}
}
5. 遍历:

keySet:获取 map 中的所有key,因为 key 不能重复,所以是一个 set。

values: 获取 map 的值。因为值可能是重复的,所以 values 返回的是 collection 类型。

entrySet:返回由 entry 对象构成的 set,因为 key 不会重复,所以 entry 对象也不会重复

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
package com.itguigu.Map;

import java.util.Collection;
import java.util.HashMap;
import java.util.Set;

public class TestMap {
@SuppressWarnings("all")
public static void main(String[] args) {
HashMap hashMap = new HashMap();
// 一次添加一个映射关系
hashMap.put("key", "value");
hashMap.put("key2", "value2");
hashMap.put("key3", "value3");

// 获取所有的 key
Set keySet = hashMap.keySet();
for (Object object : keySet) {
System.out.println(object); // key2 key3 key
}

// 获取所有的 value
Collection values = hashMap.values();
for (Object object : values) {
System.out.println(object); // value2 value3 value
}

// entrySet
Set entrySet = hashMap.entrySet();
for (Object object : entrySet) {
System.out.println(object); // key2=value2 key3=value3 key=value
}
}
}

常见实现类

HashMap

Hashtable

LinkedHashMap:是 HashMap 的子类,比 HashMap 多维护了添加的顺序,也就是说添加是什么顺序,遍历就是什么顺序。

TreeMap:可根据 key 进行排序。所以key必须支持排序,即实现,java.lang.Comparable 接口,或者单独为 TreeMap 定制比较器。

Properties:是 hashtable 的子类,键和值都是字符串。

HashMap 和 Hashtable 的区别:Hashtable 是旧版本的,线程安全的,HashMap 是比 Hashtable 新的,是线程不安全的。Hashtable 的 key 和 value 都不允许为 null。HashMap 的 key 和 value 都允许为 null。